import pandas as pd
import numpy as np
import Core.MongoDB as MongoDB
import Core.IO as IO
import Core.Gadget as Gadget
import datetime
import math
# import statsmodels.api as sm  #统计运算
import scipy.stats as scs  # 科学计算
import matplotlib.pyplot as plt  # 绘图
import scipy.optimize as sco
from AssetAllocation.Common import *
from AssetAllocation.BasicOptimize import BasicOptimize
from PerformanceMeasure.measure_config import *
ANNUALIZED_DAYS = APPROX_BDAYS_PER_YEAR


class Markowitz_back(BasicOptimize):
    def __init__(self, symbol_list, annualized_days):
        self.symbol_list = symbol_list
        self.num_assets = len(self.symbol_list)
        self.annualized_days = annualized_days
        self.correlation_matrix = None
        self.annualized_covariance_matrix = None
        self.annualized_return = None
        self.annualized_volatility = None
        self.boundaries = ()
        self.instrument_type_constraints = {}  # {"股票":(1,0), "债券": (1,0), "货币": (1,0), "海外":(1,0), "商品":(1,0)}
        self.instrument_type_list = []

    def initialize_with_returns(self, df_returns):
        self.symbol_list = df_returns.columns.tolist()
        #
        mean = df_returns.mean() * self.annualized_days
        self.annualized_return = mean.tolist()
        #
        sd = df_returns.std() * np.sqrt(self.annualized_days)
        self.annualized_volatility = sd.tolist()
        #
        self.correlation_matrix = df_returns.corr()
        # print(self.correlation_matrix)
        self.annualized_covariance_matrix = df_returns.cov() * self.annualized_days
        #
        self.num_assets = len(self.annualized_return)

    def initialize_with_params(self, expected_return_array, expected_volatility_array, correlation_matrix):
        self.correlation_matrix = correlation_matrix
        self.annualized_return = expected_return_array
        self.annualized_volatility = expected_volatility_array
        self.annualized_covariance_matrix = Calc_Covariance_Matrix(correlation_matrix, expected_volatility_array)
        #
        self.num_assets = len(self.annualized_return)

    def initialize_with_params_return_and_cov(self, expected_return_array, covariance):
        self.annualized_return = expected_return_array
        self.annualized_covariance_matrix = covariance
        #
        self.num_assets = len(self.annualized_return)

    def optimize(self, optimize_objective):
        #
        # print(annual_returns)
        # print(annual_stdDev)
        # print(correlation)
        #
        num_assets = len(self.annualized_return)
        #
        target_fun = MinSharpe
        if optimize_objective == "MINRISK":
            target_fun = MinVariance
        elif optimize_objective == "MAXRETURN":
            target_fun = MinReturn
        #
        cons = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons
        #
        optv = sco.minimize(target_fun, num_assets * [1. / num_assets, ],
                            args=(self.annualized_return, self.annualized_covariance_matrix,),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons)
        #
        optimal_weights = optv.x
        return optimal_weights

    def optimize_target_risk(self, target_risk):
        num_assets = len(self.annualized_return)
        cons = [
            {'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
            {'type': 'eq', 'fun': lambda x: Calc_Portfolio_Profile(x, self.annualized_return, self.annualized_covariance_matrix)["Volatility"] - target_risk},
            ]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons

        optv = sco.minimize(MinReturn, num_assets * [1. / num_assets, ],
                            args=(self.annualized_return, self.annualized_covariance_matrix,),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons)
        #
        optimal_weights = optv.x
        return optimal_weights

    def optimize_target_return(self, target_return):
        num_assets = len(self.annualized_return)
        cons = [
            {'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
            {'type': 'eq', 'fun': lambda x: Calc_Portfolio_Profile(x, self.annualized_return, self.annualized_covariance_matrix)["Return"] - target_return},
            ]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons
        #
        optv = sco.minimize(MinVariance, num_assets * [1. / num_assets, ],
                            args=(self.annualized_return, self.annualized_covariance_matrix,),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons)
        #
        optimal_weights = optv.x
        return optimal_weights

    def plot_efficient_frontier(self):
        # if self.annualized_covariance_matrix != None and self.annualized_return != None:
        Efficient_Frontier(self.annualized_return, self.annualized_covariance_matrix)


class Markowitz(BasicOptimize):
    def __init__(self,
                 expected_return_array,
                 expected_volatility_array=None,
                 correlation_matrix=None,
                 covariance_matrix=[],
                 risk_free_rate=0.0):
        self.correlation_matrix = correlation_matrix
        self.annualized_return = expected_return_array
        self.annualized_volatility = expected_volatility_array
        #
        if len(covariance_matrix) > 0:
            self.annualized_covariance_matrix = covariance_matrix
        else:
            self.annualized_covariance_matrix = Calc_Covariance_Matrix(correlation_matrix, expected_volatility_array)
        #
        self.risk_free_rate = risk_free_rate
        #
        self.num_assets = len(self.annualized_return)
        self.boundaries = ()
        self.instrument_type_constraints = {}  # {"股票":(1,0), "债券": (1,0), "货币": (1,0), "海外":(1,0), "商品":(1,0)}
        self.instrument_type_list = []

    def optimize(self, optimize_objective):
        #
        num_assets = len(self.annualized_return)
        #
        target_fun = MinSharpe
        if optimize_objective == "MINRISK":
            target_fun = MinVariance
        elif optimize_objective == "MAXRETURN":
            target_fun = MinReturn
        #
        cons = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons
        init_weight = num_assets * [1. / num_assets, ]
        optv = sco.minimize(target_fun, init_weight,
                            args=(self.annualized_return, self.annualized_covariance_matrix, self.risk_free_rate),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons,
                            options={'ftol': 1e-06, 'eps': 1e-6})
        #
        optimal_weights = optv.x
        return optimal_weights

    def optimize_user_utility(self, lambda_user):
        num_assets = len(self.annualized_return)
        target_fun = MaxUserUtility
        #
        cons = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons
        #
        optv = sco.minimize(target_fun, num_assets * [1. / num_assets, ],
                            args=(self.annualized_return, self.annualized_covariance_matrix, lambda_user, self.risk_free_rate),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons)
        #
        optimal_weights = optv.x
        return optimal_weights

    def optimize_target_risk(self, target_risk):
        num_assets = len(self.annualized_return)
        cons = [
            {'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
            {'type': 'eq', 'fun': lambda x: Calc_Portfolio_Profile(x, self.annualized_return, self.annualized_covariance_matrix)["Volatility"] - target_risk},
            ]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons

        optv = sco.minimize(MinReturn, num_assets * [1. / num_assets, ],
                            args=(self.annualized_return, self.annualized_covariance_matrix, self.risk_free_rate),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons)
        #
        optimal_weights = optv.x
        return optimal_weights

    def optimize_target_return(self, target_return):
        num_assets = len(self.annualized_return)
        cons = [
            {'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
            {'type': 'eq', 'fun': lambda x: Calc_Portfolio_Profile(x, self.annualized_return, self.annualized_covariance_matrix)["Return"] - target_return},
            ]
        bnds, instrument_type_cons = self._process_constraints()
        cons += instrument_type_cons
        #
        optv = sco.minimize(MinVariance, num_assets * [1. / num_assets, ],
                            args=(self.annualized_return, self.annualized_covariance_matrix, self.risk_free_rate),
                            method='SLSQP',
                            bounds=bnds,
                            constraints=cons)
        #
        optimal_weights = optv.x
        return optimal_weights

    def plot_efficient_frontier(self):
        # if self.annualized_covariance_matrix != None and self.annualized_return != None:
        Efficient_Frontier(self.annualized_return, self.annualized_covariance_matrix)


# 读取Return 数据
def Load_Data(database, symbol_list, datetime1, datetime2):
    data = Load_Multi_DailyBar_DataFrame(database, symbol_list, datetime1, datetime2)
    # data = data.drop(['DateTime'], axis=1)
    returns = np.log(data / data.shift(1))  # 转换成对数收益率
    # print(returns)
    return returns


def Build_Random_Weights(count):
    weights = np.random.random(count)
    weights /= np.sum(weights)
    return weights


def Calc_Portfolio_Profile(weights_list, exp_returns, covariance_matrix, risk_free_rate=0.0):
    #
    if isinstance(weights_list, list):
        weights = np.asarray(weights_list)
    else:
        weights = weights_list

    # ---Portfolio Return Sum---
    pf_return = np.sum(exp_returns * weights)
    pf_variance = np.dot(weights.T, np.dot(covariance_matrix, weights))

    pf_std_deviation = np.sqrt(pf_variance)
    sharpe = (pf_return - risk_free_rate) / pf_std_deviation

    # if np.isnan(pf_return):
    #     pf_return = None
    # if np.isnan(pf_std_deviation):
    #     pf_std_deviation = None
    # if np.isnan(sharpe):
    #     sharpe = None

    # print( "ret " + str(port_return) + " sd " + str(port_stdDeviation))
    return {"Return": pf_return, "Volatility": pf_std_deviation, "Sharpe": sharpe}


def Calc_Portfolio_Profile_v2(weights_list, exp_returns, exp_volatility, correlation_matrix, risk_free_rate=0.0):
    covariance_matrix = Calc_Covariance_Matrix(correlation_matrix, exp_volatility)
    return Calc_Portfolio_Profile(weights_list, exp_returns, covariance_matrix, risk_free_rate=risk_free_rate)


def MinSharpe(weights, exp_returns, covariance_matrix, risk_free_rate=0):
    # print("w", weights)
    value = Calc_Portfolio_Profile(weights, exp_returns, covariance_matrix, risk_free_rate)["Sharpe"]
    return -1 * value


def MinVariance(weights, exp_returns, covariance_matrix, risk_free_rate=0):
    return Calc_Portfolio_Profile(weights, exp_returns, covariance_matrix, risk_free_rate)["Volatility"]


def MinReturn(weights, exp_returns, covariance_matrix, risk_free_rate=0):
    value = Calc_Portfolio_Profile(weights, exp_returns, covariance_matrix, risk_free_rate)["Return"]
    return -1 * value


def MaxUserUtility(weights, exp_returns, covariance_matrix, lambda_user, risk_free_rate=0):
    w = weights
    er = exp_returns
    sigma = covariance_matrix
    #
    exp_return = np.dot(np.array(w).T, er) - lambda_user / 2.0 * np.dot(np.dot(np.array(w).T, sigma), np.array(w))
    return -1 * exp_return


def MaxSharpe_Optimize(exp_returns, covariance_matrix):
    print(" Sharpe最大 ")
    #
    num_assets = len(exp_returns)
    cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bnds = tuple((0, 1) for x in range(num_assets))
    opts = sco.minimize(MinSharpe, num_assets * [1. / num_assets, ],
                        args=(exp_returns, covariance_matrix, ),
                        method='SLSQP',
                        bounds=bnds,
                        constraints=cons)
    print(opts)
    # [ 0.31296679,  0.03839007,  0.64446006,  0.00418308]
    return opts.x


def MinVariance_Optimize(exp_returns, covariance_matrix):
    print(" 方差最小 ")
    #
    num_assets = len(exp_returns)
    cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bnds = tuple((0, 1) for x in range(num_assets))
    optv = sco.minimize(MinVariance, num_assets * [1./num_assets, ],
                        args=(exp_returns, covariance_matrix, ),
                        method='SLSQP',
                        bounds=bnds,
                        constraints=cons)
    print(optv)
    # [ 0.34351965,  0.19567223,  0.25471517,  0.20609294]
    return optv.x


def MaxReturn_Optimize(exp_returns, covariance_matrix):
    print(" 收益最大 ")
    #
    num_assets = len(exp_returns)
    cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bnds = tuple((0, 1) for x in range(num_assets))
    optv = sco.minimize(MinReturn, num_assets * [1./num_assets, ],
                        args=(exp_returns, covariance_matrix, ),
                        method='SLSQP',
                        bounds=bnds,
                        constraints=cons)
    print(optv)
    return optv.x


# ---Monte Carlo---
def MonteCarlo(exp_returns, covariance_matrix, iteraton=1000):
    port_returns = []
    port_variances = []
    num_assets = len(exp_returns)
    #
    for p in range(iteraton):
        weights = Build_Random_Weights(num_assets)
        #print(weights)
        values = Calc_Portfolio_Profile(weights, exp_returns, covariance_matrix)
        # print("Return " + str(values[0]) + " SD " + str(values[1]))
        port_returns.append(values["Return"])
        port_variances.append(values["Volatility"])

    port_returns = np.array(port_returns)
    port_variances = np.array(port_variances)

    #risk_free = 0.04
    #plt.figure(figsize=(8, 4))
    #plt.scatter(port_variances, port_returns, c=(port_returns - risk_free) / port_variances, marker='o')
    #plt.grid(True)
    #plt.xlabel('excepted volatility')
    #plt.ylabel('expected return')
    #plt.colorbar(label='Sharpe ratio')
    #plt.show()

    return port_returns, port_variances


def Efficient_Frontier(exp_returns, covariance_matrix):
    #
    num_assets = len(exp_returns)
    #
    maxSharp_weight = MaxSharpe_Optimize(exp_returns, covariance_matrix)
    maxSharp_profile = Calc_Portfolio_Profile(maxSharp_weight, exp_returns, covariance_matrix)
    print(maxSharp_profile)
    #
    minVariance_weight = MinVariance_Optimize(exp_returns, covariance_matrix)
    minVariance_profile = Calc_Portfolio_Profile(minVariance_weight, exp_returns, covariance_matrix)
    print(minVariance_profile)
    #
    # 在不同目标收益率水平（target_returns）循环时，最小化的一个约束条件会变化。
    target_returns = np.linspace(minVariance_profile["Return"], maxSharp_profile["Return"], 10)
    # target_returns = np.linspace(0.01, 0.10, 10)
    target_variance = []

    for tar in target_returns:
        bnds = tuple((0, 1) for x in range(num_assets))
        # #1 Constraint 指定目標收益率
        # #2 Constraint 总权重为1
        # print(tar)
        cons = ({'type': 'eq', 'fun': lambda x: Calc_Portfolio_Profile(x, exp_returns, covariance_matrix)["Return"] - tar},
                {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
        res = sco.minimize(MinVariance, num_assets * [1./num_assets, ],
                           args=(exp_returns, covariance_matrix,),
                           method='SLSQP',
                           bounds=bnds, constraints=cons)
        target_variance.append(res['fun'])
        print("Frontier Ret: " + str(tar) + " sd " + str(res['fun']))

    target_variance = np.array(target_variance)

    # ---Generate Random Pf---
    port_returns, port_variances = MonteCarlo(exp_returns, covariance_matrix, 1000)

    plt.figure(figsize=(8, 4))

    # 圆圈：蒙特卡洛随机产生的组合分布
    # risk_free = 0.04
    # plt.scatter(port_variances, port_returns, c=(port_returns - risk_free) / port_variances, marker='o')
    plt.scatter(port_variances, port_returns, c=port_returns / port_variances, marker='o')

    # 叉号：有效前沿
    plt.scatter(target_variance, target_returns, c=target_returns / target_variance, marker='x')

    # 红星：标记最高sharpe组合
    plt.plot(maxSharp_profile["Volatility"], maxSharp_profile["Return"], 'r*', markersize=15.0)

    # 黄星：标记最小方差组合
    plt.plot(minVariance_profile["Volatility"], minVariance_profile["Return"], 'y*', markersize=15.0)

    plt.grid(True)
    plt.xlabel('expected volatility')
    plt.ylabel('expected return')
    plt.colorbar(label='Sharpe ratio')
    plt.show()


def Test_Efficient_Frontier(exp_returns, covariance_matrix):
    #
    num_assets = len(exp_returns)
    max_return_weight = MaxReturn_Optimize(exp_returns, covariance_matrix)
    max_return_profile = Calc_Portfolio_Profile(max_return_weight, exp_returns, covariance_matrix)
    print("max return", max_return_profile)
    #
    maxSharp_weight = MaxSharpe_Optimize(exp_returns, covariance_matrix)
    maxSharp_profile = Calc_Portfolio_Profile(maxSharp_weight, exp_returns, covariance_matrix)
    print("max sharp", maxSharp_profile)
    #
    minVariance_weight = MinVariance_Optimize(exp_returns, covariance_matrix)
    minVariance_profile = Calc_Portfolio_Profile(minVariance_weight, exp_returns, covariance_matrix)
    print("min variance", minVariance_profile)
    #
    # 在不同目标收益率水平（target_returns）循环时，最小化的一个约束条件会变化。
    target_returns = np.linspace(minVariance_profile["Return"], max_return_profile["Return"], 10)
    # target_returns = np.linspace(0.01, 0.10, 10)
    target_variance = []

    for tar in target_returns:
        bnds = tuple((0, 1) for x in range(num_assets))
        # #1 Constraint 指定目標收益率
        # #2 Constraint 总权重为1
        # print(tar)
        cons = ({'type': 'eq', 'fun': lambda x: Calc_Portfolio_Profile(x, exp_returns, covariance_matrix)["Return"] - tar},
                {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
        res = sco.minimize(MinVariance, num_assets * [1./num_assets, ],
                           args=(exp_returns, covariance_matrix,),
                           method='SLSQP',
                           bounds=bnds, constraints=cons)
        target_variance.append(res['fun'])
        print("Frontier Ret: " + str(tar) + " sd " + str(res['fun']))

    target_variance = np.array(target_variance)


def Calc_Asset_Type_Weights(weights, asset_type_list):
    asset_type_weights = {}
    num_assets = len(weights)
    for i in range(num_assets):
        # 循环资产的权重和类型
        weight = weights[i]
        asset_type = asset_type_list[i]
        #
        if asset_type not in asset_type_weights.keys():
            asset_type_weights[asset_type] = 0
        #
        asset_type_weights[asset_type] += weight
    #
    return asset_type_weights


def Calc_Weight_by_Instrument_Type(weights, instrument_type_list, target_instrument_type, target_weight, min_max):
    asset_type_weights = {}
    num_assets = len(weights)
    total_weights = 0
    for i in range(num_assets):
        # 循环资产的权重和类型
        weight = weights[i]
        asset_type = instrument_type_list[i]
        #
        if asset_type == target_instrument_type:
            total_weights += weight
    #
    if min_max == "Max":
        return target_weight - total_weights
    elif min_max == "Min":
        return total_weights - target_weight


# 以参数方式输入，测试
def Test_With_Params():
    # annual_returns = pd.Series([0.10, 0.16, 0.04, 0.058])
    # annual_std_dev = pd.Series([0.20, 0.30, 0.02, 0.038])
    # correlation = [[1.000000, 0.8404, -0.0256, -0.0065],
    #                [0, 1.000000, -0.0377, 0.0020],
    #                [0, 0, 1.000000, 0.2268],
    #                [0, 0, 0, 1.000000]]

    annual_returns = [0.6588, 0.7278]
    annual_std_dev = [0.1634, 0.2551]
    correlation = [[1.0, 0.7507],
                   [0.7507, 1.0]]


    # 用df_Return 初始化
    mv = Markowitz(annual_returns, annual_std_dev, correlation, )
    optimal_weights = mv.optimize(optimize_objective="MAXSHARPE")
    print(optimal_weights)

    target_ret = 0.64
    for i in range(10):
        target_ret += 0.01
        # print(ret)
        weights = mv.optimize_target_return(target_ret)
        # print(weights)

        a_profile = Calc_Portfolio_Profile(weights, annual_returns, mv.annualized_covariance_matrix)
        # print(maxSharp_profile)

        a_profile["w1"] = weights[0]
        a_profile["w2"] = weights[1]
        print(a_profile)

    mv = Markowitz_back(symbol_list=["A","B","C","D"], annualized_days=252)
    mv.initialize_with_params(annual_returns, annual_std_dev, correlation)
    optimal_weights = mv.optimize(optimize_objective="MAXSHARPE")
    print(optimal_weights)

    mv.plot_efficient_frontier()

def Test_Covariance():
    x = [2.1, 2.5, 4.0, 3.6]
    y = [8, 12, 14, 10]

    data = []
    for i in range(len(x)):
        data.append([x[i], y[i]])
    df = pd.DataFrame(data)
    print(df)

    print(df.cov() * 252)

    mean = df.mean()
    correlation = df.corr()
    std = df.std() * math.sqrt(252)
    covariance_matrix = Calc_Covariance_Matrix(correlation, std)
    print(covariance_matrix)


def Test_With_Random_Data_Step_by_Step():

    # ---NUMBER OF ASSETS---
    numAssets = 4

    # ---NUMBER OF OBSERVATIONS---
    numObs = 6

    # ---Geneate Test Data---
    returns = Generate_Random_Data(numAssets, numObs)

    # ---Mean---
    annual_returns = returns.mean() * 252
    print("Means")
    print(annual_returns)

    # ---Sd---
    annual_stdDev = returns.std() * math.sqrt(252)
    print("Std Dev")
    print(annual_stdDev)

    # ---Correlation---
    print("Correlation")
    df_correlation = returns.corr()
    correlation = df_correlation.values
    print(df_correlation)

    # ---Covariance---
    print("Covariance")
    df_covariance = returns.cov() *252
    print(df_covariance)

    covariance_matrix = Calc_Covariance_Matrix(correlation, annual_stdDev)
    print(covariance_matrix)

    # ---Sharpe最大---
    MaxSharpe_Optimize(annual_returns, covariance_matrix)

    # ---方差最小---
    MinVariance_Optimize(annual_returns, covariance_matrix)

    profile = Calc_Portfolio_Profile(pd.Series([0, 0, 1, 0]), annual_returns, covariance_matrix)
    print(profile)

    profile = Calc_Portfolio_Profile(pd.Series([0.00711223, 0.01257539, 0.73401904, 0.24629335]), annual_returns,
                                     covariance_matrix)
    print(profile)
    profile = Calc_Portfolio_Profile(pd.Series([1.13640387e-02, 1.89735380e-19, 8.38853312e-01, 1.49782649e-01]),
                                     annual_returns, covariance_matrix)
    print(profile)

    Efficient_Frontier(annual_returns, covariance_matrix)


def Test_Compare_Initialize_with_Return_and_Params():
    #
    numAssets = 4
    numObs = 6
    df_returns = Generate_Random_Data(numAssets, numObs)
    profile = Calc_Return_Volatility_Correlation(df_returns, annualized_days=252)

    # 用df_Return 初始化
    mv = Markowitz(expected_return_array=profile["Returns"],
                   expected_volatility_array=profile["Volatilities"],
                   correlation_matrix=profile["Correlation"],
                   risk_free_rate=0)
    optimal_weights = mv.optimize(optimize_objective="MAXSHARPE")
    print(optimal_weights)

    mv3 = Markowitz(expected_return_array=mv.annualized_return,
                    covariance_matrix=mv.annualized_covariance_matrix,
                    risk_free_rate=0)
    optimal_weights = mv3.optimize(optimize_objective="MAXSHARPE")
    print(optimal_weights)

    #
    annual_returns = (df_returns.mean() * 252).tolist()
    annual_stdDev = (df_returns.std() * math.sqrt(252)).tolist()
    correlation = df_returns.corr().values
    mv2 = Markowitz_back(symbol_list=[], annualized_days=252)
    # print(annual_returns)
    # print(annual_stdDev)
    # print(correlation)

    mv2.initialize_with_params(annual_returns, annual_stdDev, correlation)
    optimal_weights2 = mv2.optimize(optimize_objective="MAXSHARPE")
    print(optimal_weights2)

    mv3 = Markowitz_back(symbol_list=[], annualized_days=252)
    mv3.initialize_with_returns(df_returns)
    optimal_weights3 = mv3.optimize(optimize_objective="MAXSHARPE")
    print(optimal_weights3)

    a = 0


# 随机序列，测试
def Test_With_Random_Data():
    #
    numAssets = 4
    numObs = 6
    df_returns = Generate_Random_Data(numAssets, numObs)
    print(df_returns)

    profile = Calc_Return_Volatility_Correlation(df_returns, annualized_days=252)

    # 用df_Return 初始化
    mv = Markowitz(expected_return_array=profile["Returns"],
                   expected_volatility_array=profile["Volatilities"],
                   correlation_matrix=profile["Correlation"],
                   risk_free_rate=0)
    #
    optimal_weights1 = mv.optimize(optimize_objective="MAXSHARPE")
    print("MAXSHARPE", optimal_weights1)
    optimal_weights2 = mv.optimize(optimize_objective="MAXRETURN")
    print("MAXRETURN", optimal_weights2)
    optimal_weights3 = mv.optimize_target_risk(0.15)
    print(optimal_weights3)
    #
    mv.plot_efficient_frontier()


# 真实序列，测试
def Test_With_Real_Data(database):
    #
    datetime2 = datetime.datetime(2020, 1, 1)
    datetime1 = datetime2 - datetime.timedelta(days=365*2)

    # symbols = ["000852.SH", "000300.SH", "H11001.CSI", "AU.SHF", "SPX.GI"]
    symbols = ["AU.SHF", "H11001.CSI", "000300.SH", "SPX.GI"]
    df_returns = Load_Data(database, symbols, datetime1, datetime2)
    print(df_returns)

    profile = Calc_Return_Volatility_Correlation(df_returns, annualized_days=252)

    # 用df_Return 初始化
    mv = Markowitz(expected_return_array=profile["Returns"],
                   expected_volatility_array=profile["Volatilities"],
                   correlation_matrix=profile["Correlation"],
                   risk_free_rate=0)
    #
    print(mv.annualized_return)
    print(mv.annualized_volatility)
    optimal_weights_max_sharpe = mv.optimize(optimize_objective="MAXSHARPE")
    print("MAXSHARPE", Calc_Portfolio_Profile(optimal_weights_max_sharpe, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights_max_sharpe)

    print("")
    optimal_weights_max_return = mv.optimize(optimize_objective="MAXRETURN")
    print("MAXRETURN", Calc_Portfolio_Profile(optimal_weights_max_return, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights_max_return)

    print("")
    optimal_weights_min_risk = mv.optimize(optimize_objective="MINRISK")
    print("MINRISK", Calc_Portfolio_Profile(optimal_weights_min_risk, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights_min_risk)
    #
    print("")
    print("Target Risk 0.05")
    optimal_weights0 = mv.optimize_target_risk(0.05)
    print(Calc_Portfolio_Profile(optimal_weights0, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights0)

    print("")
    print("Target Risk 0.10")
    optimal_weights1 = mv.optimize_target_risk(0.10)
    print(Calc_Portfolio_Profile(optimal_weights1, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights1)

    print("")
    print("Target Risk 0.15")
    optimal_weights2 = mv.optimize_target_risk(0.15)
    print(Calc_Portfolio_Profile(optimal_weights2, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights2)

    print("")
    print("Target Risk 0.20")
    optimal_weights3 = mv.optimize_target_risk(0.20)
    print(Calc_Portfolio_Profile(optimal_weights3, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights3)

    #
    print("")
    print("Target Return 0.05")
    optimal_weights0 = mv.optimize_target_return(0.05)
    print(Calc_Portfolio_Profile(optimal_weights0, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights0)

    print("")
    print("Target Return 0.10")
    optimal_weights1 = mv.optimize_target_return(0.10)
    print(Calc_Portfolio_Profile(optimal_weights1, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights1)

    print("")
    print("Target Return 0.15")
    optimal_weights2 = mv.optimize_target_return(0.15)
    print(Calc_Portfolio_Profile(optimal_weights2, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights2)

    print("")
    print("Target Return 0.20")
    optimal_weights3 = mv.optimize_target_return(0.20)
    print(Calc_Portfolio_Profile(optimal_weights3, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights3)
    #
    # mv.plot_efficient_frontier()


def Test_Constraints(database):
    #
    datetime2 = datetime.datetime(2020, 10, 20)
    datetime1 = datetime2 - datetime.timedelta(days=30*6)

    symbols = ["000001.SH", "399001.SZ"]
    df_returns = Load_Data(database, symbols, datetime1, datetime2)
    print(df_returns)
    profile = Calc_Return_Volatility_Correlation(df_returns, annualized_days=252)
    print(profile)

    # 用df_Return 初始化
    mv = Markowitz(expected_return_array=profile["Returns"],
                   expected_volatility_array=profile["Volatilities"],
                   correlation_matrix=profile["Correlation"],
                   risk_free_rate=0)
    # mv.set_symbol_constraints([(0.5, 0.7), (0, 0.4)])
    mv.set_symbol_constraints([(0, 1), (0, 1)])
    # mv.set_instrument_type_constraints({"Stock": [0.2, 1], "Bond": [0, 0.5]}, ["Comm", "Bond", "Stock", "Oversea"])
    # mv.set_asset_type_constraints({"债券": [0.2, 0.5]}, ["商品","债券","股票","海外"])
    # mv.set_asset_type_constraints({"股票": [0.1, 0.5]}, ["商品","债券","股票","海外"])

    #
    print(mv.annualized_return)
    print(mv.annualized_volatility)
    optimal_weights_max_sharpe = mv.optimize(optimize_objective="MAXSHARPE")
    print("MAXSHARPE", Calc_Portfolio_Profile(optimal_weights_max_sharpe, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights_max_sharpe)

    print("")
    optimal_weights_max_return = mv.optimize(optimize_objective="MAXRETURN")
    print("MAXRETURN", Calc_Portfolio_Profile(optimal_weights_max_return, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights_max_return)

    print("")
    optimal_weights_min_risk = mv.optimize(optimize_objective="MINRISK")
    print("MINRISK", Calc_Portfolio_Profile(optimal_weights_min_risk, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights_min_risk)
    #
    print("")
    print("Target Risk 0.05")
    optimal_weights0 = mv.optimize_target_risk(0.05)
    print(Calc_Portfolio_Profile(optimal_weights0, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights0)

    print("")
    print("Target Risk 0.10")
    optimal_weights1 = mv.optimize_target_risk(0.10)
    print(Calc_Portfolio_Profile(optimal_weights1, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights1)

    print("")
    print("Target Risk 0.15")
    optimal_weights2 = mv.optimize_target_risk(0.15)
    print(Calc_Portfolio_Profile(optimal_weights2, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights2)

    print("")
    print("Target Risk 0.20")
    optimal_weights3 = mv.optimize_target_risk(0.20)
    print(Calc_Portfolio_Profile(optimal_weights3, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights3)

    #
    print("")
    print("Target Return 0.05")
    optimal_weights0 = mv.optimize_target_return(0.05)
    print(Calc_Portfolio_Profile(optimal_weights0, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights0)

    print("")
    print("Target Return 0.10")
    optimal_weights1 = mv.optimize_target_return(0.10)
    print(Calc_Portfolio_Profile(optimal_weights1, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights1)

    print("")
    print("Target Return 0.15")
    optimal_weights2 = mv.optimize_target_return(0.15)
    print(Calc_Portfolio_Profile(optimal_weights2, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights2)

    print("")
    print("Target Return 0.20")
    optimal_weights3 = mv.optimize_target_return(0.20)
    print(Calc_Portfolio_Profile(optimal_weights3, mv.annualized_return, mv.annualized_covariance_matrix))
    print(optimal_weights3)
    #
    # mv.plot_efficient_frontier()


def Test_Constraints_Violate(database):
    #
    datetime2 = datetime.datetime(2020, 5, 20)
    datetime1 = datetime2 - datetime.timedelta(days=30*6)

    symbols = ["000300.SH", "H11001.CSI", "CRB.GI", "H11025.CSI"]
    df_returns = Load_Data(database, symbols, datetime1, datetime2)
    print(df_returns)
    profile = Calc_Return_Volatility_Correlation(df_returns, annualized_days=252)
    print(profile)

    # 用df_Return 初始化
    mv = Markowitz(expected_return_array=profile["Returns"],
                   expected_volatility_array=profile["Volatilities"],
                   correlation_matrix=profile["Correlation"],
                   risk_free_rate=0.015)

    mv.set_symbol_constraints([(0.05, 1.0), (0.05, 1.0), (0.05, 1.0), (0.05, 1.0)])
    # mv.set_instrument_type_constraints({"Stock": [0.2, 1], "Bond": [0, 0.5]}, ["Comm", "Bond", "Stock", "Oversea"])
    # mv.set_asset_type_constraints({"债券": [0.2, 0.5]}, ["商品","债券","股票","海外"])
    # mv.set_asset_type_constraints({"股票": [0.1, 0.5]}, ["商品","债券","股票","海外"])

    #
    print(mv.annualized_return)
    print(mv.annualized_volatility)
    optimal_weights_max_sharpe = mv.optimize(optimize_objective="MAXSHARPE")

    #
    print(optimal_weights_max_sharpe)


#
def test_calc_portfolio_profile():
    weights_list = [0.05, 0.4, 0.4, 0.1, 0.05]
    annual_returns = pd.Series([0.02, 0.038, 0.042, 0.06, 0.15])
    annual_std_dev = pd.Series([0,    0.01,  0.005, 0.04, 0.20])
    correlation = [[1.0, 0.0, 0.0, 0.0, 0.0],
                   [0,   1.0, 0.3, 0.5, -0.3],
                   [0,   0.3, 1.0, 0.0, 0.0],
                   [0,   0.5, 0.0, 1.0, 0.2],
                   [0,  -0.3, 0,   0.2, 1.0]]

    res = Calc_Portfolio_Profile_v2(weights_list, annual_returns, annual_std_dev, correlation, risk_free_rate=0.0)
    print(res)


# 优化理财组合
def optimize_licai_portfolio():

    weights_list = [0.05, 0.4, 0.4, 0.1, 0.05]
    # 预期收益率
    annual_returns = pd.Series([0.02, 0.038, 0.042, 0.06, 0.15])

    # 预期波动率
    annual_std_dev = pd.Series([0,    0.01,  0.005, 0.04, 0.20])

    #
    

    #
    correlation = [[1.0, 0.0, 0.0, 0.0, 0.0],
                   [0,   1.0, 0.3, 0.5, -0.3],
                   [0,   0.3, 1.0, 0.0, 0.0],
                   [0,   0.5, 0.0, 1.0, 0.2],
                   [0,  -0.3, 0,   0.2, 1.0]]

    pass


if __name__ == '__main__':
    #
    # from Core.Config import *
    # pathfilename = os.getcwd() + "\..\Config\config_develop.json"
    # config = Config(pathfilename)
    # database = config.DataBase("Uni")

    # Test_Compare_Initialize_with_Return_and_Params()
    # Test_With_Random_Data_Step_by_Step()
    # Test_With_Random_Data()
    Test_With_Real_Data(database)
    Test_With_Params()
    Test_Constraints(database)
    # Test_Constraints_Violate(database)

    #
    # test_calc_portfolio_profile()
